package com.example.javafx.javafx.controller;

import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class SnakeGame extends Application {

    private static final int WIDTH = 600;
    private static final int HEIGHT = 600;
    private static final int CELL_SIZE = 20;
    private static final int ROWS = HEIGHT / CELL_SIZE;
    private static final int COLUMNS = WIDTH / CELL_SIZE;

    private List<Point> snake = new ArrayList<>();
    private Point food;
    private Direction direction = Direction.RIGHT;
    private boolean gameOver = false;
    private int score = 0;

    @Override
    public void start(Stage primaryStage) {
        Canvas canvas = new Canvas(WIDTH, HEIGHT);
        GraphicsContext gc = canvas.getGraphicsContext2D();

        Pane root = new Pane();
        root.getChildren().add(canvas);

        Scene scene = new Scene(root, WIDTH, HEIGHT);

        // 初始化蛇
        snake.add(new Point(5, 0));
        snake.add(new Point(4, 0));
        snake.add(new Point(3, 0));

        // 生成食物
        spawnFood();

        // 键盘控制
        scene.setOnKeyPressed(event -> {
            KeyCode keyCode = event.getCode();
            if (keyCode == KeyCode.UP && direction != Direction.DOWN) {
                direction = Direction.UP;
            } else if (keyCode == KeyCode.DOWN && direction != Direction.UP) {
                direction = Direction.DOWN;
            } else if (keyCode == KeyCode.LEFT && direction != Direction.RIGHT) {
                direction = Direction.LEFT;
            } else if (keyCode == KeyCode.RIGHT && direction != Direction.LEFT) {
                direction = Direction.RIGHT;
            }
        });

        primaryStage.setTitle("JavaFX贪吃蛇");
        primaryStage.setScene(scene);
        primaryStage.setResizable(false);
        primaryStage.show();

        new AnimationTimer() {
            long lastTick = 0;

            @Override
            public void handle(long now) {
                if (lastTick == 0) {
                    lastTick = now;
                    tick(gc);
                    return;
                }

                if (now - lastTick > 100_000_000) { // 控制游戏速度
                    lastTick = now;
                    tick(gc);
                }
            }
        }.start();
    }

    private void tick(GraphicsContext gc) {
        if (gameOver) {
            gc.setFill(Color.RED);
            gc.fillText("游戏结束! 得分: " + score, WIDTH / 3, HEIGHT / 2);
            return;
        }

        // 移动蛇
        Point head = snake.get(0);

        Point newHead;

        switch (direction) {
            case UP -> newHead= new Point(head.x, head.y -1);
            case DOWN ->newHead= new Point(head.x ,head.y+1);
            case LEFT->newHead= new Point(head.x-1 ,head.y);
            case RIGHT->newHead= new Point(head.x+1 ,head.y);
            default->throw new IllegalStateException("Unexpected value: " + direction );
        }

        //检查碰撞墙壁或自身
        if(newHead.x<0 ||newHead.y<0 ||newHead.x>=COLUMNS||newHead.y>=ROWS||containsSnake(newHead)){
            gameOver=true ;
            return ;
        }

        snake.add(0 ,newHead );

        //检查是否吃到食物
        if(newHead.equals(food )){
            score +=10 ;
            spawnFood ();
        }else{
            snake.remove(snake.size()-1 );//移除尾部，保持长度不变（除非吃到食物）
        }

        drawEverything(gc );
    }

    private void drawEverything(GraphicsContext gc ){
        gc.clearRect(0 ,0 ,WIDTH ,HEIGHT );

        //绘制分数
        gc.setFill(Color.BLACK );
        gc.fillText("得分: "+score ,10 ,20 );

        //绘制食物
        gc.setFill(Color.RED );
        fillCell(gc ,food );

        //绘制蛇身和头部不同颜色区分开更好看些
        for(int i=0;i<snake.size();i++){
            if(i==0){
                gc.setFill(Color.GREEN.darker());//头部深绿色
            }else{
                gc.setFill(Color.GREEN );//身体普通绿色
            }
            fillCell(gc,snake.get(i));
        }
    }

    private void fillCell(GraphicsContext gc,Point point ){
        double x=point.x*CELL_SIZE ;
        double y=point.y*CELL_SIZE ;

        double sizeWithGap=(double)(CELL_SIZE-1);//留出间隙使网格可见

        gc.fillRect(x,y,sizeWithGap,sizeWithGap );
    }

    /**
     *  随机生成食物位置（不在蛇身上）
     */
    private void spawnFood(){
        Random random=new Random();

        while(true){
            int x=random.nextInt(COLUMNS );
            int y=random.nextInt(ROWS );
            food=new Point(x,y );

            if(!containsSnake(food))break;//确保不在蛇身上才退出循环
        }
    }

    /**
     *  检查点是否在蛇身上
     */
    boolean containsSnake(Point point ){
        for(var bodyPart :snake ){
            if(bodyPart.equals(point ))return true ;
        }

        return false ;
    }

    enum Direction { UP,DOWN,LEFT,RIGHT}

    record Point(int x,int y){}

    public static void main(String[]args){ launch(args);}
}
